package com.lblin.method.util;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;

/**
 * 动态枚举生成类
 * @author vi
 *
 */
public class EunmUtil {
	
	private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

	/**
	 * 
	 * @param enumType 枚举类
	 * @param enumName 枚举名
	 * @param enumParams 枚举的属性，写在private构造器中的参数集合
	 */
	public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName ,Object ... enumParams) {
		 
        // 0. Sanity checks
        if (!Enum.class.isAssignableFrom(enumType)) {
            throw new RuntimeException("class " + enumType + " is not an instance of Enum");
        }
        // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
        Field valuesField = null;
        Field[] fields = enumType.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().contains("$VALUES")) {
                valuesField = field;
                break;
            }
        }
        AccessibleObject.setAccessible(new Field[] { valuesField }, true);
 
        try {
 
            // 2. Copy it
            T[] previousValues = (T[]) valuesField.get(enumType);
            List values = new ArrayList(Arrays.asList(previousValues));
            int length = enumParams.length/2;
            Class[] clazzs = new Class<?>[length];
            Object[] params = new Object[length];
            //将自定义的构造器参数传入
            for(int i=0;i<length;i++) {
            	clazzs[i] = getClassByType(enumParams[2*i].toString());
            	params[i] = enumParams[2*i+1];
            }
 
            // 3. build new enum
            T newValue = (T) makeEnum(enumType, // The target enum class
                    enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
                    values.size(),
                    clazzs, // can be used to pass values to the enum constuctor
                    params); // can be used to pass values to the enum constuctor
 
            // 4. add new value
            values.add(newValue);
 
            // 5. Set new values field
            setFailsafeFieldValue(valuesField, null,
                    values.toArray((T[]) Array.newInstance(enumType, 0)));
 
            // 6. Clean enum cache
            cleanEnumCache(enumType);
 
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        }
    }
	
	private static Object makeEnum(Class<?> enumClass, String value, int ordinal,
	        Class<?>[] additionalTypes, Object[] additionalValues) throws Exception {
	    Object[] parms = new Object[additionalValues.length + 2];
	    parms[0] = value;
	    parms[1] = Integer.valueOf(ordinal);
	    System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
	    return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
	}
	 
	private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass,
	        Class<?>[] additionalParameterTypes) throws NoSuchMethodException {
	    Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
	    parameterTypes[0] = String.class;
	    parameterTypes[1] = int.class;
	    System.arraycopy(additionalParameterTypes, 0,
	            parameterTypes, 2, additionalParameterTypes.length);
	    return reflectionFactory.newConstructorAccessor(
	            enumClass.getDeclaredConstructor(parameterTypes));
	}
	
	/**
	 * 支持传入的构造器参数类型
	 * @param type
	 * @return
	 */
	private static Class getClassByType(String type) {
		Class clazz = null;
		switch (type) {
		case "int":
			clazz = int.class;
			break;
		case "String":
			clazz = String.class;
			break;

		default:
			try {
				clazz = Class.forName(type);
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			break;
		}
		return clazz;
	}
	
	private static void setFailsafeFieldValue(Field field, Object target, Object value)
	         throws NoSuchFieldException, IllegalAccessException {
	 
	    // let's make the field accessible
	    field.setAccessible(true);
	 
	    // next we change the modifier in the Field instance to
	    // not be final anymore, thus tricking reflection into
	    // letting us modify the static final field
	    Field modifiersField = Field.class.getDeclaredField("modifiers");
	    modifiersField.setAccessible(true);
	    int modifiers = modifiersField.getInt(field);
	 
	    // blank out the final bit in the modifiers int
	    modifiers &= ~Modifier.FINAL;
	    modifiersField.setInt(field, modifiers);
	 
	    FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
	    fa.set(target, value);
	}
	
	private static void cleanEnumCache(Class<?> enumClass)
	        throws NoSuchFieldException, IllegalAccessException {
	    blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 
	    blankField(enumClass, "enumConstants"); // IBM JDK
	}
	 
	private static void blankField(Class<?> enumClass, String fieldName)
	        throws NoSuchFieldException, IllegalAccessException {
	    for (Field field : Class.class.getDeclaredFields()) {
	        if (field.getName().contains(fieldName)) {
	            AccessibleObject.setAccessible(new Field[] { field }, true);
	            setFailsafeFieldValue(field, enumClass, null);
	            break;
	        }
	    }
	}
	
	public static void main(String[] args) {
//		addEnum(Test.class, "b","String","动态参数");
//		System.out.println(Test.values()[1].getDesc());
//		try {
//			Method m = EunmUtil.class.getDeclaredMethod("getClassByType", String.class);
//			
//			long result = SizeAgent.getSize(m);
//			
//			result = checkSize(result,m);
//			
//			System.out.println(result);
//		} catch (Exception e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}
	
//	static class Test1{
//		String b;
//		Test2 t = new Test2();
//	}
//	
//	static class Test2{
//		String a;
//	}
//
//	static List<String> filterField = new ArrayList<String>(){
//		{
//			add("soleInstance");
//			add("reflectionFactoryAccessPerm");
//		}
//	};
//	
//	private static long checkSize(long result, Object m) {
//		// TODO Auto-generated method stub
//		Field[] fields = m.getClass().getDeclaredFields();
//		for(Field field : fields) {
//			field.setAccessible(true);
//			boolean isp = field.getType().isPrimitive();//是否基本数据类型
//			if(!isp&&!filterField.contains(field.getName())) {
//				//只处理大于4字节的类型
//				//System.out.println(field.getName());
//				long l = 0;
//				Object o = null;
//				try {
//					o = field.get(m);
//					l = SizeAgent.getSize(o);
//				} catch (IllegalArgumentException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} catch (IllegalAccessException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				if(l>4) {
//					result = result + l;
//					//递归遍历
//					result = checkSize(result,o);
//				}
//			}
//		}
//		return result;
//	}
//	
	
}
